#!/usr/bin/env python3
"""
数据预处理管道 - Web界面应用

基于Flask的Web界面，提供图形化的数据预处理操作
"""

import os
import sys
import json
import uuid
import threading
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Optional

from flask import Flask, render_template, request, jsonify, send_file, redirect, url_for, flash
from flask_socketio import SocketIO, emit
from werkzeug.utils import secure_filename
import pandas as pd

# 添加项目根目录到Python路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

# 导入核心组件
from core.file_manager import FileManager
from core.data_loader import DataLoader
from core.configuration_manager import ConfigurationManager
from core.combination_generator import CombinationGenerator
from core.processing_pipeline import ProcessingPipeline
from core.output_manager import OutputManager
from preprocessors.registry import PreprocessorRegistry
from utils.progress_tracker import ProgressTracker

# Flask应用配置
app = Flask(__name__)
app.config['SECRET_KEY'] = 'preprocessing_pipeline_secret_key'
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024  # 100MB max file size

# SocketIO for real-time updates
socketio = SocketIO(app, cors_allowed_origins="*")

# 确保上传目录存在
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs('output', exist_ok=True)

# 全局变量存储应用状态
app_state = {
    'current_data': None,
    'current_file_path': None,
    'current_config': None,
    'processing_status': 'idle',  # idle, processing, completed, error
    'processing_results': None,
    'session_id': None
}

# 初始化核心组件
file_manager = FileManager()
data_loader = DataLoader()
config_manager = ConfigurationManager()
combination_generator = CombinationGenerator()
output_manager = OutputManager()

# 初始化预处理器注册表
preprocessor_registry = PreprocessorRegistry()
preprocessor_registry.auto_discover_processors()

# 初始化处理管道
processing_pipeline = ProcessingPipeline(preprocessor_registry)


@app.route('/')
def index():
    """主页面"""
    return render_template('index.html')


@app.route('/api/methods')
def get_methods():
    """获取可用的预处理方法"""
    try:
        methods = preprocessor_registry.get_available_methods()
        method_names = preprocessor_registry.get_method_names()
        
        method_list = []
        for method_id in sorted(methods):
            method_list.append({
                'id': method_id,
                'name': method_names.get(method_id, f'方法 {method_id}'),
                'description': f'预处理方法 {method_id}'
            })
        
        return jsonify({
            'success': True,
            'methods': method_list
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        })


@app.route('/api/upload', methods=['POST'])
def upload_file():
    """处理文件上传"""
    try:
        if 'file' not in request.files:
            return jsonify({
                'success': False,
                'error': '没有选择文件'
            })
        
        file = request.files['file']
        if file.filename == '':
            return jsonify({
                'success': False,
                'error': '没有选择文件'
            })
        
        if file and allowed_file(file.filename):
            # 生成安全的文件名
            filename = secure_filename(file.filename)
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"{timestamp}_{filename}"
            
            file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
            file.save(file_path)
            
            # 验证和加载文件
            try:
                file_manager.validate_excel_file(file_path)
                data = data_loader.load_excel(file_path)
                data_loader.validate_data_structure(data)
                
                # 更新应用状态
                app_state['current_data'] = data
                app_state['current_file_path'] = file_path
                app_state['session_id'] = str(uuid.uuid4())
                
                # 生成数据预览
                preview = generate_data_preview(data)
                
                return jsonify({
                    'success': True,
                    'message': '文件上传成功',
                    'filename': filename,
                    'data_info': {
                        'rows': len(data),
                        'columns': len(data.columns),
                        'preview': preview
                    },
                    'session_id': app_state['session_id']
                })
                
            except Exception as e:
                # 删除无效文件
                if os.path.exists(file_path):
                    os.remove(file_path)
                return jsonify({
                    'success': False,
                    'error': f'文件验证失败: {str(e)}'
                })
        
        return jsonify({
            'success': False,
            'error': '不支持的文件格式，请上传 .xlsx 或 .xls 文件'
        })
        
    except Exception as e:
        return jsonify({
            'success': False,
            'error': f'上传失败: {str(e)}'
        })


@app.route('/api/configure', methods=['POST'])
def configure_processing():
    """配置处理参数"""
    try:
        if app_state['current_data'] is None:
            return jsonify({
                'success': False,
                'error': '请先上传数据文件'
            })
        
        data = request.get_json()
        layer_count = data.get('layer_count')
        layer_selections = data.get('layer_selections', [])
        
        # 验证配置
        if not isinstance(layer_count, int) or layer_count < 1 or layer_count > 10:
            return jsonify({
                'success': False,
                'error': '层数必须在1-10之间'
            })
        
        if len(layer_selections) != layer_count:
            return jsonify({
                'success': False,
                'error': f'层选择数量({len(layer_selections)})与层数({layer_count})不匹配'
            })
        
        # 验证方法选择
        available_methods = preprocessor_registry.get_available_methods()
        for i, layer_methods in enumerate(layer_selections):
            if not layer_methods:
                return jsonify({
                    'success': False,
                    'error': f'第{i+1}层必须至少选择一个方法'
                })
            
            for method_id in layer_methods:
                if method_id not in available_methods:
                    return jsonify({
                        'success': False,
                        'error': f'方法ID {method_id} 不可用'
                    })
        
        # 计算组合数量
        total_combinations = 1
        for layer_methods in layer_selections:
            total_combinations *= len(layer_methods)
        
        # 创建配置对象
        from core.models import ProcessingConfiguration
        
        output_dir = output_manager.create_output_directory(
            Path(app_state['current_file_path']).stem
        )
        
        config = ProcessingConfiguration(
            input_file=app_state['current_file_path'],
            layer_count=layer_count,
            layer_selections=layer_selections,
            output_directory=output_dir
        )
        
        app_state['current_config'] = config
        
        return jsonify({
            'success': True,
            'message': '配置成功',
            'config': {
                'layer_count': layer_count,
                'layer_selections': layer_selections,
                'total_combinations': total_combinations,
                'output_directory': output_dir
            }
        })
        
    except Exception as e:
        return jsonify({
            'success': False,
            'error': f'配置失败: {str(e)}'
        })


@app.route('/api/process', methods=['POST'])
def start_processing():
    """开始处理"""
    try:
        if app_state['current_data'] is None:
            return jsonify({
                'success': False,
                'error': '请先上传数据文件'
            })
        
        if app_state['current_config'] is None:
            return jsonify({
                'success': False,
                'error': '请先配置处理参数'
            })
        
        if app_state['processing_status'] == 'processing':
            return jsonify({
                'success': False,
                'error': '正在处理中，请等待完成'
            })
        
        # 启动后台处理线程
        processing_thread = threading.Thread(
            target=background_processing,
            args=(app_state['session_id'],)
        )
        processing_thread.daemon = True
        processing_thread.start()
        
        app_state['processing_status'] = 'processing'
        
        return jsonify({
            'success': True,
            'message': '开始处理',
            'session_id': app_state['session_id']
        })
        
    except Exception as e:
        return jsonify({
            'success': False,
            'error': f'启动处理失败: {str(e)}'
        })


@app.route('/api/status')
def get_status():
    """获取当前状态"""
    return jsonify({
        'success': True,
        'status': {
            'has_data': app_state['current_data'] is not None,
            'has_config': app_state['current_config'] is not None,
            'processing_status': app_state['processing_status'],
            'session_id': app_state['session_id']
        }
    })


@app.route('/api/results')
def get_results():
    """获取处理结果"""
    if app_state['processing_results'] is None:
        return jsonify({
            'success': False,
            'error': '没有处理结果'
        })
    
    return jsonify({
        'success': True,
        'results': app_state['processing_results']
    })


@app.route('/api/download/<path:filename>')
def download_file(filename):
    """下载结果文件"""
    try:
        if app_state['current_config'] is None:
            return jsonify({'error': '没有可用的结果文件'}), 404
        
        file_path = os.path.join(app_state['current_config'].output_directory, filename)
        
        if not os.path.exists(file_path):
            return jsonify({'error': '文件不存在'}), 404
        
        return send_file(file_path, as_attachment=True)
        
    except Exception as e:
        return jsonify({'error': str(e)}), 500


@app.route('/api/open-directory')
def open_directory():
    """打开输出目录"""
    try:
        if app_state['current_config'] is None:
            return jsonify({
                'success': False,
                'error': '没有可用的输出目录'
            })
        
        output_dir = app_state['current_config'].output_directory
        
        if not os.path.exists(output_dir):
            return jsonify({
                'success': False,
                'error': '输出目录不存在'
            })
        
        # 根据操作系统打开文件夹
        import platform
        import subprocess
        
        system = platform.system()
        
        try:
            if system == "Windows":
                os.startfile(output_dir)
            elif system == "Darwin":  # macOS
                subprocess.run(["open", output_dir])
            else:  # Linux
                subprocess.run(["xdg-open", output_dir])
            
            return jsonify({
                'success': True,
                'message': '已打开输出目录',
                'directory': output_dir
            })
            
        except Exception as e:
            return jsonify({
                'success': False,
                'error': f'无法打开目录: {str(e)}'
            })
        
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        })


def allowed_file(filename):
    """检查文件扩展名是否允许"""
    ALLOWED_EXTENSIONS = {'xlsx', 'xls'}
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


def generate_data_preview(data, max_rows=5):
    """生成数据预览"""
    try:
        preview_data = data.head(max_rows)
        
        # 转换为可序列化的格式
        preview = {
            'columns': list(data.columns),
            'rows': []
        }
        
        for _, row in preview_data.iterrows():
            preview['rows'].append([
                str(val) if pd.notna(val) else '' 
                for val in row.values
            ])
        
        return preview
        
    except Exception as e:
        return {'error': str(e)}


def background_processing(session_id):
    """后台处理函数"""
    try:
        if app_state['session_id'] != session_id:
            return  # 会话已过期
        
        # 生成组合
        combinations = combination_generator.generate_combinations(
            app_state['current_config'].layer_selections
        )
        
        # 发送开始处理的消息
        socketio.emit('processing_started', {
            'total_combinations': len(combinations),
            'session_id': session_id
        })
        
        # 进度回调函数
        def progress_callback(current, total, combination_id):
            progress = {
                'current': current,
                'total': total,
                'percentage': (current / total) * 100,
                'combination_id': combination_id,
                'session_id': session_id
            }
            socketio.emit('processing_progress', progress)
        
        # 执行批处理
        batch_result = processing_pipeline.batch_process(
            data=app_state['current_data'],
            combinations=combinations,
            progress_callback=progress_callback
        )
        
        # 保存结果
        saved_files = save_processing_results(batch_result)
        
        # 准备结果数据
        results = {
            'total_combinations': batch_result.total_combinations,
            'successful_combinations': batch_result.successful_combinations,
            'failed_combinations': batch_result.failed_combinations,
            'success_rate': batch_result.success_rate,
            'processing_time': batch_result.total_processing_time,
            'output_directory': app_state['current_config'].output_directory,
            'files': saved_files,
            'session_id': session_id
        }
        
        app_state['processing_results'] = results
        app_state['processing_status'] = 'completed'
        
        # 发送完成消息
        socketio.emit('processing_completed', results)
        
    except Exception as e:
        app_state['processing_status'] = 'error'
        error_msg = str(e)
        
        socketio.emit('processing_error', {
            'error': error_msg,
            'session_id': session_id
        })


def save_processing_results(batch_result):
    """保存处理结果并返回文件列表"""
    saved_files = []
    method_names = preprocessor_registry.get_method_names()
    
    for combination_id, result in batch_result.results.items():
        if result.success and result.processed_data is not None:
            try:
                # 生成方法名称列表
                combination_method_names = [
                    method_names.get(mid, f"方法{mid}") 
                    for mid in result.combination
                ]
                
                # 获取输出文件路径
                output_path = output_manager.get_full_output_path(
                    directory=app_state['current_config'].output_directory,
                    combination_id=combination_id,
                    method_names=combination_method_names
                )
                
                # 保存文件
                success = output_manager.save_excel(result.processed_data, output_path)
                
                if success:
                    file_info = {
                        'filename': os.path.basename(output_path),
                        'combination_id': combination_id,
                        'methods': combination_method_names,
                        'size': os.path.getsize(output_path) if os.path.exists(output_path) else 0
                    }
                    saved_files.append(file_info)
                    
            except Exception as e:
                print(f"保存文件失败 {combination_id}: {e}")
    
    return saved_files


@socketio.on('connect')
def handle_connect():
    """处理WebSocket连接"""
    print('客户端已连接')


@socketio.on('disconnect')
def handle_disconnect():
    """处理WebSocket断开"""
    print('客户端已断开')


def open_browser():
    """延迟打开浏览器"""
    import time
    import webbrowser
    import threading
    
    def delayed_open():
        time.sleep(1.5)  # 等待服务器启动
        try:
            webbrowser.open('http://localhost:5000')
            print("✓ 已自动打开浏览器")
        except Exception as e:
            print(f"⚠️  无法自动打开浏览器: {e}")
            print("请手动访问: http://localhost:5000")
    
    browser_thread = threading.Thread(target=delayed_open)
    browser_thread.daemon = True
    browser_thread.start()


if __name__ == '__main__':
    print("🚀 启动数据预处理管道Web界面...")
    print("=" * 50)
    print("🌐 本地访问: http://localhost:5000")
    print("📱 局域网访问: http://你的IP地址:5000")
    print("🛑 停止服务: 按 Ctrl+C")
    print("=" * 50)
    
    # 自动打开浏览器
    open_browser()
    
    try:
        socketio.run(app, debug=False, host='0.0.0.0', port=5000)
    except KeyboardInterrupt:
        print("\n\n👋 Web服务已停止")
    except Exception as e:
        print(f"\n❌ 启动失败: {e}")
        print("\n💡 可能的解决方案:")
        print("1. 检查端口5000是否被占用")
        print("2. 以管理员权限运行")
        print("3. 检查防火墙设置")